﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Ordinaire.Db.Linq;
using Ordinaire.Model;
using Ordinaire.Taxonomy.Model;
using Ordinaire.Tool;

namespace Ordinaire.Taxonomy
{
    public class Librarian
    {
        #region Variables

        /// <summary>
        /// Singleton instance
        /// </summary>
        protected static readonly Librarian _instance = new Librarian();

        #endregion

        #region Constructor

        /// <summary>
        /// Hide constructor
        /// </summary>
        private Librarian() { }

        #endregion

        #region Properties

        /// <summary>
        /// Returns singleton instance
        /// </summary>
        public static Librarian Instance
        {
            get { return _instance; }
        }

        #endregion

        public bool Tag(IUnique<int> item, string name)
        {
            if (item == null)
            {
                throw new ArgumentNullException(String.Format(ExceptionMessage.IsNullOrEmpty, "item"));
            }

            if (item.Id < 0)
            {
                throw new ArgumentException(String.Format(ExceptionMessage.EqualToOrLessThanZero, "item.Id"));
            }

            if (String.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException(String.Format(ExceptionMessage.IsNullOrEmpty, "name"));
            }

            int vocabId = GetVocabId(name);
            if (vocabId == 0)
            {
                Vocab vocab = AddVocab(name);
                vocabId = vocab.Id;
            }

            // Gather all required table information for the INSERT query.
            string itemTableName = Reflection.GetTableAttribute(item);

            Tag tag = new Tag();
            string tagTableName         = Reflection.GetTableAttribute(tag);
            string tagItemIdColumnName  = Reflection.GetColumnAttribute(tag, "ItemId");
            string tagVocabIdColumnName = Reflection.GetColumnAttribute(tag, "VocabId");
            // Transform them according to the specified item tablename
            tagTableName        = tagTableName.Replace("Item", itemTableName);
            tagItemIdColumnName = tagItemIdColumnName.Replace("Item", itemTableName);

            // Prepare INSERT query
            StringBuilder sbQuery = new StringBuilder();
            sbQuery.Append("INSERT INTO ");
            sbQuery.Append(tagTableName);
            sbQuery.Append(" (");
            sbQuery.Append(tagItemIdColumnName);
            sbQuery.Append(", ");
            sbQuery.Append(tagVocabIdColumnName);
            sbQuery.Append(", CreatedBy");
            sbQuery.Append(") VALUES ({0}, {1}, {2})");

            using (OrdinaireContext context = new OrdinaireContext())
            {
                int affectedRows = context.ExecuteCommand(sbQuery.ToString(), item.Id, vocabId, "admin");
                return affectedRows == 1;
            }
        }

        /// <summary>
        /// Add new vocabulary into the database.
        /// </summary>
        /// <param name="vocab">vocabulary</param>
        /// <returns></returns>
        protected Vocab AddVocab(string vocab)
        {
            if (String.IsNullOrEmpty(vocab))
            {
                throw new ArgumentNullException(String.Format(ExceptionMessage.IsNullOrEmpty, "vocab"));
            }

            using (OrdinaireContext context = new OrdinaireContext())
            {
                Vocab v = new Vocab();
                v.Name = vocab.ToLower();

                context.Vocabs.InsertOnSubmit(v);
                context.SubmitChanges();

                return v;
            }
        }

        /// <summary>
        /// Returns the id of the specified vocabulary name.
        /// </summary>
        /// <param name="vocab">vocabulary</param>
        /// <returns>id of the vocab else 0 (zero) when not found</returns>
        protected int GetVocabId(string vocab)
        {
            if (String.IsNullOrEmpty(vocab))
            {
                throw new ArgumentNullException(String.Format(ExceptionMessage.IsNullOrEmpty, "vocab"));
            }

            int vocabId = 0;

            using (OrdinaireContext context = new OrdinaireContext())
            {
                var query = from v in context.Vocabs
                            where v.Name == vocab.ToLower()
                            select v.Id;

                vocabId = query.Single<int>();
            }

            return vocabId;
        }
    }
}
